package com.ltf.springframework.support;

import com.ltf.springframework.annotation.Autowired;
import com.ltf.springframework.annotation.Controller;
import com.ltf.springframework.annotation.Service;
import com.ltf.springframework.beans.BeanDefinition;
import com.ltf.springframework.core.BeanFactory;
import com.ltf.springframework.beans.BeanWrapper;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyApplicationContext implements BeanFactory {
    private String[] configLocations;

    private BeanDefinitionReader reader;

    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private Map<String,Object> beanCacheMap = new ConcurrentHashMap<>();

    private Map<String,BeanWrapper> beanWrapperMap = new ConcurrentHashMap<>();

    public MyApplicationContext(String... config) {
        configLocations = config;
        // refresh
        refresh();
    }

    private void refresh() {
        // 定位
        reader = new BeanDefinitionReader(configLocations);

        // 加载
        List<String> beanNames = reader.loadBeanDefinition();

        // 注册
        doRegisty(beanNames);

        // 依赖注入
        doAutowired();

    }

    private void doAutowired() {
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }

        for (Map.Entry<String, BeanWrapper> beanWrapperEntry : beanWrapperMap.entrySet()) {
            populate(beanWrapperEntry.getKey(),beanWrapperEntry.getValue().getWrapperInstance());
        }
    }

    private void populate(String className, Object wrapperInstance) {
        Class<?> clazz = wrapperInstance.getClass();
        if(!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))){
            return;
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(Autowired.class)){
                String autowreidName = declaredField.getAnnotation(Autowired.class).value().trim();
                // 如果没有指定注入名称，默认首小写
                if ("".equals(autowreidName)){
                    autowreidName = declaredField.getType().getName();
                }

                Object instance = beanWrapperMap.get(autowreidName).getWrapperInstance();
                declaredField.setAccessible(true);
                try {
                    declaredField.set(wrapperInstance,instance);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }            }

        }


    }

    private void doRegisty(List<String> beanNames) {
        for (String beanName : beanNames) {
            try {
                Class<?> clazz = Class.forName(beanName);
                // 如果是接口不注册
                if(clazz.isInterface()){
                    continue;
                }
                BeanDefinition beanDefinition = reader.registyBean(beanName);
                beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);

                Class<?>[] interfaces = clazz.getInterfaces();
                if (interfaces != null){
                    for (Class<?> anInterface : interfaces) {
                        beanDefinitionMap.put(anInterface.getName(),beanDefinition);
                    }
                }
                // 容器到此初始化完毕
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)){
            return null;
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        String className = beanDefinition.getClassName();

        Object object = instanitionBean(beanDefinition);
        if (object == null){
            return null;
        }
        try {
            Class<?> clazz = Class.forName(className);
            BeanWrapper wrapper = new BeanWrapper(clazz.newInstance());
            beanWrapperMap.put(beanName,wrapper);
            return this.beanWrapperMap.get(beanName).getWrapperInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  null;
    }

    private synchronized Object instanitionBean(BeanDefinition beanDefinition) {
        String className = beanDefinition.getClassName();
        try {
            Class<?> clazz = Class.forName(className);
            if (this.beanCacheMap.containsKey(className)){
                return beanCacheMap.get(className);
            }else{
                Object o = clazz.newInstance();
                beanCacheMap.put(className,o);
                return o;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
}
